/*
 * Copyright (C) Michael Martin & Antonio Carrozzini (TBJD)
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */
package org.tbjd.client.beans;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.tbjd.client.managers.TaskManager;
import org.tbjd.client.states.AbstractTaskState;
import org.tbjd.client.states.StateDurationByRatios;
import org.tbjd.client.states.StateBeginFrozenASAP;

/**
 * <p>
 * A Task represents an action that could be associated with a {@link org.tbjd.client.beans.Resource resource}.
 * </p>
 * 
 * @author Michael Martin
 * @author Antonio Carrozzini
 * @version 1.0
 * 
 */
public class Task {

	static final public int TASK = 0;
	static final public int PHASE = 1;
	static final public int MILESTONE = 2;

	private TaskManager dateManager = new TaskManager();

	/**
	 * Unique id of the Task (same as id in DB)
	 */
	private int id;
	/**
	 * Short name for the Task
	 */
	private String name;
	/**
	 * The date at which the task begins.
	 */
	private Date beginDate;
	/**
	 * The date at which the task ends.
	 */
	private Date endDate;
	/**
	 * <p>
	 * The amount of time necessary to accomplish the task
	 * </p>
	 * <p>
	 * Reference unit is hour
	 * </p>
	 */
	private float duration;
	/**
	 * <p>
	 * Human/days
	 * </p>
	 * <p>
	 * Reference unit is hour
	 * </p>
	 */
	private float effort;
	/**
	 * The amount of work done for the task (in percent).
	 */
	private int progress;
	/**
	 * The vertical position of the task in a GUI ({@link org.tbjd.client.guis.gantt.GanttGui Gantt} or TasksList)
	 */
	private int level;
	/**
	 * If its a Task, a Phase or a Milestone
	 */
	private int type;
	/**
	 * If this task is in a phase, the parent is the phase
	 */
	private Task parent;
	/**
	 * The children of this task if its a phase
	 */
	private ArrayList<Task> lstChildren = new ArrayList<Task>();
	/**
	 * List of successors
	 */
	private List<Task> successors = new ArrayList<Task>();
	/**
	 * List of predecessors
	 */
	private List<Task> predecessors = new ArrayList<Task>();
	/**
	 * <p>To know whether the task duration is computed by dates or by ratios</p>
	 * <p>True if duration mode formulas is by ratios</p>
	 */
	private boolean durationModeByRatios;
	/**
	 * If true, the BeginDate is frozen, else the EndDate is frozen
	 */
	private boolean beginDateFrozen;
	/**
	 * If true, the task is in ASAP chaining mode, else its in SPECIFIED (SPEC) chaining mode
	 */
	private boolean chainingModeASAP;
	/**
	 * <p>{@link org.tbjd.client.states.AbstractTaskState State} of the Task</p>
	 * <p>Transient field</p>
	 */
	private AbstractTaskState state;
	/**
	 * List of associated {@link org.tbjd.client.beans.Resource resources}
	 */
	private List<Resource> resources;
	/**
	 * Status for collapse state
	 */
	private boolean collapsed;

	/**
	 * Default constructor with state set
	 */
	public Task() {
		setBeginDateFrozen(true);
		setChainingModeASAP(true);
		setDurationModeByRatios(true);
		setState(new StateBeginFrozenASAP(new StateDurationByRatios()));
	}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public Date getBeginDate() {
		return beginDate;
	}

	public void setBeginDate(Date beginDate) {
		this.beginDate = beginDate;
	}

	public Date getEndDate() {
		return endDate;
	}

	public void setEndDate(Date endDate) {
		this.endDate = endDate;
	}

	public float getDuration() {
		return duration;
	}

	public void setDuration(float duration) {
		this.duration = duration;
	}

	public int getProgress() {
		return progress;
	}

	public void setProgress(int progress) {
		this.progress = progress;
	}

	public int getLevel() {
		return level;
	}

	public void setLevel(int level) {
		this.level = level;
	}

	public int getType() {
		return type;
	}

	public void setType(int type) {
		this.type = type;
	}

	public Task getParent() {
		return parent;
	}

	public void setParent(Task parent) {
		this.parent = parent;
	}

	public ArrayList<Task> getLstChildren() {
		return lstChildren;
	}

	public void setLstChildren(ArrayList<Task> lstChildren) {
		this.lstChildren = lstChildren;
	}

	public void addChild(Task child) {
		if (!this.lstChildren.contains(child)) {
			this.lstChildren.add(child);
		}
	}

	public void removeChild(Task child) {
		this.lstChildren.remove(child);
	}

	public float getEffort() {
		return effort;
	}

	public void setEffort(float effort) {
		this.effort = effort;
	}

	public boolean isDurationModeByRatios() {
		return durationModeByRatios;
	}

	public void setDurationModeByRatios(boolean computedDurationMode) {
		this.durationModeByRatios = computedDurationMode;
	}

	public boolean isBeginDateFrozen() {
		return beginDateFrozen;
	}

	public void setBeginDateFrozen(boolean beginDateFrozen) {
		this.beginDateFrozen = beginDateFrozen;
	}

	public boolean isChainingModeASAP() {
		return chainingModeASAP;
	}

	public void setChainingModeASAP(boolean isASAPChainingMode) {
		this.chainingModeASAP = isASAPChainingMode;
	}

	public List<Task> getSuccessors() {
		return successors;
	}

	public void setSuccessors(List<Task> successors) {
		this.successors = successors;

		for (Task successor : successors) {
			if (!successor.getSuccessors().contains(this)) {
				successor.addPredecessor(this);
			}
		}
	}

	public void addSuccessor(Task successor) {
		if (this.successors == null) {
			this.successors = new ArrayList<Task>();
		}

		if (!this.successors.contains(successor)) {
			this.successors.add(successor);

			successor.addPredecessor(this);
		}
	}

	public void removeSuccessor(Task successor) {
		if (this.successors != null) {
			this.successors.remove(successor);
		}

		successor.removePredecessor(this);
	}

	public List<Task> getPredecessors() {
		return predecessors;
	}

	public void setPredecessors(List<Task> predecessors) {
		this.predecessors = predecessors;

		for (Task predecessor : predecessors) {
			predecessor.addSuccessor(this);
		}
	}

	public void addPredecessor(Task predecessor) {
		if (this.predecessors == null) {
			this.predecessors = new ArrayList<Task>();
		}

		if (!this.predecessors.contains(predecessor)) {
			this.predecessors.add(predecessor);
			predecessor.addSuccessor(this);
		}
	}

	public void removePredecessor(Task predecessor) {
		if (this.predecessors != null) {
			this.predecessors.remove(predecessor);
		}

		predecessor.removeSuccessor(this);
	}

	// TODO to see how to implement this
	public int getDaysLeft() {
		return -1;
	}

	public AbstractTaskState getState() {
		return state;
	}

	public void setState(AbstractTaskState state) {
		this.state = state;
	}

	public List<Resource> getResources() {
		return resources;
	}

	public void setResources(List<Resource> resources) {
		this.resources = resources;

		// mirror method
		for (Resource resource : resources) {
			resource.addTaskMirror(this);
		}
	}

	public void setResource(Resource resource) {
		if (this.resources == null) {
			this.resources = new ArrayList<Resource>();
		}

		this.resources.clear();
		this.resources.add(resource);

		// mirror method
		resource.addTaskMirror(this);
	}

	public void addResources(List<Resource> resources) {
		if (this.resources == null) {
			this.resources = new ArrayList<Resource>();
		}
		this.resources.addAll(resources);

		// mirror method
		for (Resource resource : resources) {
			resource.addTaskMirror(this);
		}
	}

	public void addResource(Resource resource) {
		if (this.resources == null) {
			this.resources = new ArrayList<Resource>();
		}
		this.resources.add(resource);

		// mirror method
		resource.addTaskMirror(this);
	}

	public void removeResource(Resource resource) {
		if (this.resources != null) {
			this.resources.remove(resource);
		}

		// mirror method
		resource.removeTaskMirror(this);
	}

	public void removeResources(List<Resource> resources) {
		if (this.resources != null) {
			this.resources.removeAll(resources);
		}

		for (Resource resource : resources) {
			// mirror method
			resource.removeTaskMirror(this);
		}
	}

	public void addResourceMirror(Resource resource) {
		if (this.resources == null) {
			this.resources = new ArrayList<Resource>();
		}
		this.resources.add(resource);
	}

	public void removeResourceMirror(Resource resource) {
		if (this.resources != null) {
			this.resources.remove(resource);
		}
	}

	public String toString() {
		super.toString();
		StringBuffer sb = new StringBuffer();
		sb.append("[");
		sb.append(getId());
		sb.append("] ");
		sb.append(getName());
		sb.append(" ");
		sb.append(getBeginDate());
		sb.append(" - ");
		sb.append(getEndDate());
		sb.append(" ");
		sb.append(getDuration());
		sb.append("[h] ");
		sb.append(getEffort());
		sb.append(" ASAP ? ");
		sb.append(getState().isASAPChainingMode());
		sb.append(" " + getLevel());
		sb.append("[lev] ");

		return sb.toString();
	}
	
	//TODO implements hashcode
	public boolean equals(Object obj) {
		Task task = (Task) obj;
		return new Long(id).equals(new Long(task.getId()));
	}

	public boolean isCollapsed() {
		return collapsed;
	}

	public void setCollapsed(boolean collapsed) {
		this.collapsed = collapsed;
	}
	
}
